package com.zjdiepu.www.commpent;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.stereotype.Component;

@Component("redisCache")
public class RedisCache {
	
	private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
	
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	
	public <T> T execute(RedisCallback<T> callback){
		return redisTemplate.execute(callback);
	}
	
	public void put(String key, Object value) {
		final String keyf = key; 
		final Object valuef = value;
		
		redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				byte[] valueb = toByteArray(valuef);
				connection.set(keyb, valueb);
				return 1L;
			}
		});
	}

	public void put(String key, Object value, long expire, TimeUnit timeUnit) {
		final String keyf = key;
		final Object valuef = value;
		final long expiref = TimeoutUtils.toSeconds(expire, timeUnit);

		redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				byte[] valueb = toByteArray(valuef);
				connection.set(keyb, valueb);
				
				if (expiref > 0) {
					connection.expire(keyb, expiref);
				}
				return 1L;
			}
		});
	}

	@SuppressWarnings("unchecked")
	public <T> T get(String key) {
		Object object = null;
        final String keyf = key;
        object = redisTemplate.execute(new RedisCallback<Object>() {  
            public Object doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                byte[] keyByte = keyf.getBytes();  
                byte[] value = connection.get(keyByte);  
                if (value == null) {  
                    return null;  
                }  
                return toObject(value);  
  
            }  
        });  
        return (T) object;
	}

	public void expire(String key, long expire, TimeUnit timeUnit) {
		final String keyf = key;
		final long expiref = TimeoutUtils.toSeconds(expire, timeUnit);
		redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyByte = keyf.getBytes(); 
				connection.expire(keyByte, expiref);
				return null;
			}
		});
	}

	public long del(String key) {
		final String keyf = key;  
        return redisTemplate.execute(new RedisCallback<Long>() {  
            public Long doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                return connection.del(keyf.getBytes());  
            }
        });
	}

	public void clear() {
		redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				  connection.flushDb();
				  return true;
			}
		});
	}
	
	public long hDels(String key) {
		final String keyf = key; 
		return redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				return connection.del(keyb);
			}
		});
	}
	
	public long hDel(String key, String field) {
		final String keyf = key; 
		final String fieldf = field;
		return redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				byte[] fieldb = fieldf.getBytes();
				return connection.hDel(keyb, fieldb);
			}
		});
	}
	
	public boolean hSet(String key, String field, Object value) {
		final String keyf = key; 
		final String fieldf = field;
		final Object valuef = value;
		
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				byte[] fieldb = fieldf.getBytes();
				byte[] valueb = toByteArray(valuef);
				return connection.hSet(keyb, fieldb, valueb);
			}
		});
	}
	
	@SuppressWarnings("unchecked")
	public <T> T hGet(String key, String field) {
		final String keyf = key; 
		final String fieldf = field;
		return redisTemplate.execute(new RedisCallback<T>() {
			public T doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				byte[] fieldb = fieldf.getBytes();
				byte[] value = connection.hGet(keyb, fieldb);
				if(value == null) {
					return null;
				}
				return (T)toObject(value);
			}
		});
	}
	
	/**
	 * 描述 : <Object转byte[]>.
	 * 
	 * @param obj
	 * @return
	 */
	public static byte[] toByteArray(Object obj) {
		byte[] bytes = null;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(obj);
			oos.flush();
			bytes = bos.toByteArray();
			oos.close();
			bos.close();
		} catch (IOException ex) {
			logger.error("toByteArray catch IOException.{}", ex);
		}
		return bytes;
	}

	/**
	 * 描述 : <byte[]转Object>.
	 * 
	 * @param bytes
	 * @return
	 */
	public static Object toObject(byte[] bytes) {
		Object obj = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			obj = ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException ex) {
			logger.error("toObject catch IOException.{}", ex);
		} catch (ClassNotFoundException ex) {
			logger.error("toObject catch ClassNotFoundException.{}", ex);
		}
		return obj;
	}

}
